#include <map>

#include "lexer.h"
#include "fstream"

namespace
{

std::map<char, Token> GenerateMapToken()
{
	std::map<char, Token> map;
	
	map['+'] = Token::ADD;
	map['-'] = Token::SUB;
	map['>'] = Token::NEXT;
	map['<'] = Token::PREV;
	map['.'] = Token::PRINT;
	map[','] = Token::SCAN;
	map['['] = Token::IZGN;
	map[']'] = Token::IZGB;
	
	return map;
}

}

LexerError Lexer::GenerateTokenStream()
{
	if (!ReadSourceFile())
		return m_error_map[LexerErrorCode::FILE_NOT_FOUND];
	if (!CheckSyntax())
		return m_error_map[LexerErrorCode::SYNTAX_ERROR];
	if (!CreateTokenStream())
		return m_error_map[LexerErrorCode::INVALID_SYMBOL];

	return m_error_map[LexerErrorCode::SUCCESS];
}

std::list<Token>& Lexer::GetTokenStream()
{
	return m_token_stream;
}
/////////////////////////////////////////////////////////////////////
// Private functions
/////////////////////////////////////////////////////////////////////

bool Lexer::ReadSourceFile()
{
	std::ifstream in(m_source_file);
	if (!in)
		return false;	

	std::getline(in, m_source_code);

	if (m_source_code.length() == 0)
		return false;

	return true;
}

bool Lexer::CheckSyntax()
{
	// calculate count izgb and count izgn. these values must be equal
	const int count_izgb = std::count(m_source_code.begin(), m_source_code.end(), '[');
	const int count_izgn = std::count(m_source_code.begin(), m_source_code.end(), ']');
	if (count_izgb != count_izgn)
		return false;

	// check that '[' ']' in the correct order
	int it = 0;
	int it2 = 0;

	while(1)
	{
		it = m_source_code.find('[', it);
		if (it == std::string::npos)
			return true;
		
		it2 = m_source_code.find(']', it2);
		if (it == std::string::npos)
			return false;

		if (it >= it2)
			return false;

		++it;
		++it2;
	}

	return true;
}
	
bool Lexer::CreateTokenStream()
{
	std::string::iterator it;
	for (it = m_source_code.begin(); it != m_source_code.end(); ++it)
	{
		if (!GenerateToken(*it))
			return false;
	}
	return true;
}

bool Lexer::GenerateToken(char c)
{
	static std::map<char, Token> map = GenerateMapToken();
	
	auto found = map.find(c);
	if (found == map.end())
		return false;

	m_token_stream.push_back(found->second);
	return true;
}

void Lexer::InitErrorMap()
{
	LexerError success("Lexer executed successfully\n");
	success.code = LexerErrorCode::SUCCESS;
	m_error_map[LexerErrorCode::SUCCESS] = success;

	LexerError not_found("Lexer: Source code file not found\n");
	not_found.code = LexerErrorCode::FILE_NOT_FOUND;
	m_error_map[LexerErrorCode::FILE_NOT_FOUND] = not_found;

	LexerError syntax_error("Lexer: Syntax error\n");
	syntax_error.code = LexerErrorCode::SYNTAX_ERROR;
	m_error_map[LexerErrorCode::SYNTAX_ERROR] = syntax_error;

	LexerError invalid_symbol("Lexer: Source code consist invalid symbol\n");
	invalid_symbol.code = LexerErrorCode::INVALID_SYMBOL;
	m_error_map[LexerErrorCode::INVALID_SYMBOL] = invalid_symbol;
	
}